package com.google.code.chaotic.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.allen_sauer.gwt.log.client.Log;
import com.google.code.chaotic.dto.CommuterDto;
import com.google.code.chaotic.dto.CommutingHabitDto;
import com.google.code.chaotic.dto.WayPointDto;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.geocode.DirectionQueryOptions;
import com.google.gwt.maps.client.geocode.DirectionResults;
import com.google.gwt.maps.client.geocode.Directions;
import com.google.gwt.maps.client.geocode.DirectionsCallback;
import com.google.gwt.maps.client.geocode.DirectionsPanel;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LocationCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geocode.Route;
import com.google.gwt.maps.client.geocode.StatusCodes;
import com.google.gwt.maps.client.geocode.Step;
import com.google.gwt.maps.client.geocode.Waypoint;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.datepicker.client.DateBox;
import com.google.gwt.user.datepicker.client.DateBox.DefaultFormat;

//

public class RegistrationPanel extends DockPanel {

	private CommuterDto commuterDto = null;

	/**
	 * Creating remote services' proxies to talk to the server-side services.
	 */
	private final JavaServiceAsync javaService = GWT.create(JavaService.class);
	private final CommuterServiceAsync commuterService = GWT
			.create(CommuterService.class);

	private List<Label> labels = new ArrayList<Label>();

	private static final String NATIONALITY_GROUP = "nationality";
	private static final String GENDER_GROUP = "gender";

	private RadioButton malaysianButton = new RadioButton( //
			NATIONALITY_GROUP, "Malaysian");

	private RadioButton otherButton = new RadioButton( //
			NATIONALITY_GROUP, "Other");

	private RadioButton maleButton = new RadioButton( //
			GENDER_GROUP, "Male");

	private RadioButton femaleButton = new RadioButton( //
			GENDER_GROUP, "Female");

	private static final String IC_TEXT = "I/C no.:";
	private static final String PASSPORT_TEXT = "Passport no.:";

	private Label firstNameLabel = new Label("First name:");
	private TextBox firstNameBox = new TextBox();
	private Label lastNameLabel = new Label("Last name:");
	private TextBox lastNameBox = new TextBox();
	private Label nationalityLabel = new Label("Nationality:");
	private HorizontalPanel nationalityPanel = new HorizontalPanel();
	private Label icPassportLabel = new Label(IC_TEXT);
	private TextBox icPassportBox = new TextBox();
	private Label countryLabel = new Label("Country:");
	private ListBox countryBox = new ListBox();
	private Label phoneNoLabel = new Label("Phone no.:");
	private TextBox phoneNoBox = new TextBox();
	private Label emailLabel = new Label("Email address:");
	private TextBox emailBox = new TextBox();
	private Label genderLabel = new Label("Gender:");
	private HorizontalPanel genderPanel = new HorizontalPanel();
	private Label birthDateLabel = new Label("Date of Birth:");
	private DateBox birthDateBox = new DateBox();
	private Label usernameLabel = new Label("Choose a username:");
	private TextBox usernameBox = new TextBox();
	private Label passwordLabel = new Label("Choose a password:");
	private PasswordTextBox passwordBox = new PasswordTextBox();
	private Label passRepLabel = new Label("Re-enter password:");
	private PasswordTextBox passRepBox = new PasswordTextBox();

	private FlexTable buttonPanel = new FlexTable();
	private Anchor mapsAnchor = new Anchor(
			"Click here to define the commuting route...");
	private Label linkLabel1 = new Label(
			"Then paste the link in the following box (");
	private Anchor linkLabel2 = new Anchor("Which link?");
	private Label linkLabel3 = new Label("):");
	private TextArea linkArea = new TextArea();
	private Image greenCheckImage = new Image("images/green-check.png");
	private Button checkRouteButton = new Button("Check the route");
	private Button resetButton = new Button("Reset");

	private MapWidget mapWidget = new MapWidget();
	private DirectionsPanel directionsPanel = new DirectionsPanel();

	private Button saveButton = new Button("Save");
	private Button cancelButton = new Button("Cancel");

	//

	public RegistrationPanel() {

		initProfileInfoPanel();
		initCommutingHabitsPanel();

		// Laying out Profile panel
		VerticalPanel profileVPanel = createVerticalPanel();

		FlexTable personalInfoTable = createPersonalInfoTable();
		FlexTable loginTable = createLoginTable();

		DisclosurePanel personalInfoPanel = new DisclosurePanel(
				"Personal Info.");
		personalInfoPanel.setOpen(true);
		personalInfoPanel.setContent(personalInfoTable);
		profileVPanel.add(personalInfoPanel);

		DisclosurePanel loginPanel = new DisclosurePanel("Login Info.");
		loginPanel.setOpen(false);
		loginPanel.setContent(loginTable);
		profileVPanel.add(loginPanel);

		// Laying out Habits Panel

		VerticalPanel habitsVPanel = createVerticalPanel();
		Panel routeTable = createRouteTable();
		FlexTable scheduleTable = createScheduleTable();

		DisclosurePanel routePanel = new DisclosurePanel("Commuting route");
		routePanel.setOpen(true);
		routePanel.setContent(routeTable);
		habitsVPanel.add(routePanel);

		DisclosurePanel schedulePanel = new DisclosurePanel(
				"Commuting schedule");
		schedulePanel.setOpen(false);
		schedulePanel.setContent(scheduleTable);
		habitsVPanel.add(schedulePanel);

		//

		TabPanel profileTabPanel = new TabPanel();
		profileTabPanel.add(profileVPanel, "Profile information");
		profileTabPanel.selectTab(0);

		TabPanel habitsTabPanel = new TabPanel();
		habitsTabPanel.add(habitsVPanel, "Commuting habits");
		habitsTabPanel.selectTab(0);

		// Putting Profile and Habits panels side by side
		FlexTable innerTable = new FlexTable();
		FlexCellFormatter innerTableFormatter = innerTable
				.getFlexCellFormatter();
		innerTable.setCellSpacing(5);

		innerTable.setWidget(0, 0, profileTabPanel);
		innerTable.setWidget(0, 1, habitsTabPanel);
		innerTable.setWidget(1, 1, buttonPanel);
		innerTableFormatter.setRowSpan(0, 0, 2);
		innerTableFormatter.setColSpan(0, 1, 2);
		innerTableFormatter.setVerticalAlignment(0, 0,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setVerticalAlignment(0, 1,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setVerticalAlignment(1, 1,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setWidth(0, 1, "100%");
		innerTableFormatter.setHeight(0, 1, "100%");

		//

		for (Label label : labels)
			formatLabel(label);

		// Defining the overall layout
		this.setSize("100%", "100%");
		profileTabPanel.setHeight("100%");
		habitsTabPanel.setSize("100%", "100%");
		habitsVPanel.setSize("100%", "100%");
		routePanel.setSize("100%", "100%");
		routeTable.setSize("92%", "100%");

		Label header = new Label("Header placeholder");
		Util.applyDottedBorder(header, "gray");
		this.add(header, DockPanel.NORTH);

		Label footer = new Label("Footer placeholder");
		Util.applyDottedBorder(footer, "gray");
		this.add(footer, DockPanel.SOUTH);

		Label menu = new Label("Menu placeholder", true);
		Util.applyDottedBorder(menu, "gray");
		this.add(menu, DockPanel.LINE_START);

		Label announcements = new Label("Placeholder", true);
		Util.applyDottedBorder(announcements, "gray");
		this.add(announcements, DockPanel.LINE_END);

		// dockPanel.add(decoratorPanel, DockPanel.CENTER);
		this.add(innerTable, DockPanel.CENTER);
	}

	public void setCommuter(CommuterDto commuterDto) {
		this.commuterDto = commuterDto;
	}

	public CommuterDto getCommuter() {
		return commuterDto;
	}

	private void initCommuter() {
		if (commuterDto == null)
			commuterDto = new CommuterDto();
	}

	private VerticalPanel createVerticalPanel() {
		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setSpacing(5);
		vPanel.getElement().getStyle().setMargin(5, Unit.PX);
		return vPanel;
	}

	private void initCommutingHabitsPanel() {
		// Google Maps' "Get Directions" for Kuala Lumpur
		String mapsUrl = "http://www.google.com.my/maps?ie=UTF8&ll=3.139003,101.686855&spn=0.296871,0.441513&z=11";

		mapsAnchor.setHref(mapsUrl);
		mapsAnchor.setTarget("_new");
		// final DialogBox dialogBox = createDialogBox();
		// mapsAnchor.addClickHandler(new ClickHandler() {
		// @Override
		// public void onClick(ClickEvent event) {
		// dialogBox.center();
		// dialogBox.show();
		// }
		// });

		linkArea.setVisibleLines(2);
		linkArea.setWidth("90%");

		linkArea.setReadOnly(false);
		checkRouteButton.setEnabled(true);
		greenCheckImage.setVisible(false);

		resetButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				linkArea.setReadOnly(false);
				checkRouteButton.setEnabled(true);
				greenCheckImage.setVisible(false);
				linkArea.setText(null);
			}
		});

		checkRouteButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				mapWidget.clearOverlays();
				final String link = linkArea.getText().trim();

				String logMsg = "Decoded URL by GWT: " + URL.decode(link);
				Log.debug(logMsg);

				String queryStr = link.substring(link.indexOf('?') + 1);
				Log.debug("Query substring: " + queryStr);

				HashMap<String, String> paramMap = Util
						.extractParameters(queryStr);
				final DirectionQueryOptions directionOptions = new DirectionQueryOptions(
						mapWidget, directionsPanel);

				final DirectionsCallback directionsCallback //
				= new MyDirectionsCallback(link);

				String srcAddr = paramMap.get("saddr");
				String query = paramMap.get("q");
				if (query != null) {
					loadDirections(directionOptions, query, directionsCallback);

				} else {
					String dstAddr = paramMap.get("daddr");
					String[] toAddrs = dstAddr.split(" to:");

					String[] addrs = new String[toAddrs.length + 1];
					addrs[0] = srcAddr;
					System.arraycopy(toAddrs, 0, addrs, 1, toAddrs.length);

					Waypoint[] waypoints = new Waypoint[addrs.length];
					// for (int i = 0; i < toAddrs.length; i++)
					// waypoints[i + 1] = new Waypoint(toAddrs[i]);
					// waypoints[0] = new WayPoint(srcAddr);

					Geocoder geocoder = Util.createGeocoder();
					loadDirections(directionOptions, addrs, waypoints,
							directionsCallback, geocoder);
				}
			}

			private void loadDirections(DirectionQueryOptions directionOptions,
					String query, DirectionsCallback directionsCallback) {

				Directions.load( // "from:Bukit Jalil to:3.104521,101.725159 to:Bukit Bintang",
						query, directionOptions, directionsCallback);
			}

			private void loadDirections(
					final DirectionQueryOptions directionOptions,
					String[] addrs, final Waypoint[] waypoints,
					final DirectionsCallback directionsCallback,
					Geocoder geocoder) {

				for (int i = 0; i < addrs.length; i++) {
					final int index = i;
					geocoder.getLocations(addrs[index], new LocationCallback() {
						@Override
						public void onSuccess(JsArray<Placemark> locations) {
							LatLng bestMatch = locations.get(0).getPoint();
							Log.debug("WayPoint #" + index + ": " + bestMatch);

							waypoints[index] = new Waypoint(bestMatch);
							checkOtherWaypoints();
						}

						private void checkOtherWaypoints() {
							boolean done = true;
							for (Waypoint point : waypoints)
								if (point == null)
									done = false;
							if (done) {
								Directions.loadFromWaypoints(waypoints,
										directionOptions, directionsCallback);
							}
						}

						@Override
						public void onFailure(int statusCode) {
							String message = StatusCodes.getName(statusCode)
									+ " (index #" + index + ")";
							RuntimeException ex = new RuntimeException(message);
							Log.error(message, ex);
						}
					});
				}
			}
		});
	}

	private DialogBox createDialogBox() {
		DialogBox dialogBox = new DialogBox(true, true);
		dialogBox.setText("Quick View");

		dialogBox.setGlassEnabled(true);
		dialogBox.setAnimationEnabled(true);

		String embeddedMap = "<iframe "
				+ "width=\"425\" height=\"350\" frameborder=\"0\" scrolling=\"no\" marginheight=\"0\" marginwidth=\"0\" "
				+ "src=\"http://www.google.com.my/maps?ie=UTF8&amp;ll=3.139003,101.686855&amp;spn=0.318125,0.441513&amp;z=11&amp;output=embed\">"
				+ "</iframe>"
				+ "<br />"
				+ "<small><a href=\"http://www.google.com.my/maps?ie=UTF8&amp;ll=3.139003,101.686855&amp;spn=0.318125,0.441513&amp;z=11&amp;source=embed\" style=\"color:#0000FF;text-align:left\">"
				+ "View Larger Map" + "</a>" //
				+ "</small>";
		dialogBox.setWidget(new HTML(embeddedMap));
		return dialogBox;
	}

	private void initProfileInfoPanel() {
		initFirstNameInput();
		initLastNameInput();
		initNationalityInput();
		initIcPassportInput();
		initCountryInput();
		initPhoneNoInput();
		initEmailInput();
		initGenderInput();
		initBirthDateInput();
		initUsernameInput();
		initPasswordInput();
		initPassRepInput();

		initButtonPanel();
	}

	public void requestFocus() {
		firstNameBox.setFocus(true);
	}

	private FlexTable createCustomTable() {
		FlexTable flexTable = new FlexTable();
		flexTable.getElement().getStyle().setMargin(10, Unit.PX);
		flexTable.setCellPadding(3);
		return flexTable;
	}

	private FlexTable createLoginTable() {
		FlexTable loginTable = createCustomTable();
		loginTable.setWidget(0, 0, usernameLabel);
		loginTable.setWidget(0, 1, usernameBox);
		loginTable.setWidget(1, 0, passwordLabel);
		loginTable.setWidget(1, 1, passwordBox);
		loginTable.setWidget(2, 0, passRepLabel);
		loginTable.setWidget(2, 1, passRepBox);
		return loginTable;
	}

	private FlexTable createScheduleTable() {
		FlexTable routeTable = createCustomTable();
		// TODO: include scheduling components too!
		return routeTable;
	}

	private Panel createRouteTable() {
		VerticalPanel routeVPanel = createVerticalPanel();
		routeVPanel.add(mapsAnchor);

		HorizontalPanel linkLablePanel = new HorizontalPanel();
		linkLablePanel.add(linkLabel1);
		linkLablePanel.add(linkLabel2);
		linkLablePanel.add(linkLabel3);

		routeVPanel.add(linkLablePanel);
		HorizontalPanel h1Panel = new HorizontalPanel();
		h1Panel.add(linkArea);
		h1Panel.add(greenCheckImage);
		h1Panel.setCellWidth(linkArea, "100%");
		h1Panel.setCellWidth(greenCheckImage, "50px");
		routeVPanel.add(h1Panel);

		HorizontalPanel h2Panel = new HorizontalPanel();
		Label blank = new Label();
		h2Panel.add(checkRouteButton);
		h2Panel.add(blank);
		h2Panel.add(resetButton);
		h2Panel.setCellWidth(blank, "10px");
		routeVPanel.add(h2Panel);

		return routeVPanel;
	}

	private FlexTable createPersonalInfoTable() {
		FlexTable personalInfoTable = createCustomTable();
		personalInfoTable.setWidget(0, 0, firstNameLabel);
		personalInfoTable.setWidget(0, 1, firstNameBox);
		personalInfoTable.setWidget(1, 0, lastNameLabel);
		personalInfoTable.setWidget(1, 1, lastNameBox);
		personalInfoTable.setWidget(2, 0, nationalityLabel);
		personalInfoTable.setWidget(2, 1, nationalityPanel);
		personalInfoTable.setWidget(3, 0, icPassportLabel);
		personalInfoTable.setWidget(3, 1, icPassportBox);
		personalInfoTable.setWidget(4, 0, countryLabel);
		personalInfoTable.setWidget(4, 1, countryBox);
		personalInfoTable.setWidget(5, 0, phoneNoLabel);
		personalInfoTable.setWidget(5, 1, phoneNoBox);
		personalInfoTable.setWidget(6, 0, emailLabel);
		personalInfoTable.setWidget(6, 1, emailBox);
		personalInfoTable.setWidget(7, 0, genderLabel);
		personalInfoTable.setWidget(7, 1, genderPanel);
		personalInfoTable.setWidget(8, 0, birthDateLabel);
		personalInfoTable.setWidget(8, 1, birthDateBox);
		return personalInfoTable;
	}

	private void initButtonPanel() {
		saveButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {

				initCommuter();

				commuterDto.setBirthDate(birthDateBox.getValue());

				String email = emailBox.getText().trim();
				if (email.length() == 0) {
					Window.alert("Email is required.");
					emailBox.setFocus(true);
					return;

				} else {
					commuterDto.setEmail(email);
				}
				commuterDto.setFirstName(firstNameBox.getText().trim());
				commuterDto.setLastName(lastNameBox.getText().trim());
				commuterDto.setIcPassport(icPassportBox.getText().trim());

				String country = countryBox.getValue( //
						countryBox.getSelectedIndex()).trim();
				commuterDto.setIssuingCountry(country);
				commuterDto.setMale(maleButton.getValue());

				commuterDto.setUsername(usernameBox.getText());
				String password = passwordBox.getValue();
				String passRep = passRepBox.getValue();
				if (!password.equals(passRep)) {
					Window.alert( //
							"The password and its repetition must be identical.");
					passwordBox.setFocus(true);
					return;

				} else {
					commuterDto.setPassword(password);
				}

				commuterDto.setPhoneNo(phoneNoBox.getText().trim());
				commuterService.createCommuter(commuterDto,
						new AsyncCallback<String>() {

							@Override
							public void onFailure(Throwable caught) {
								Log.error(caught.getMessage(), caught);
							}

							@Override
							public void onSuccess(String result) {
								// TODO Auto-generated method stub
								String message = "A commuter was successfully created"
										+ " with id = " + result;
								Log.debug(message);

								Window.alert(message);
							}
						});

				// TODO: Insert the date of membership on the server-side

				// TODO: Don't add the commuterDto if it's already in the table.

				// TODO: Add a button to unregister this commuterDto.
			}
		});
		cancelButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				// TODO Auto-generated method stub
			}
		});
		buttonPanel.setWidth("100%");
		buttonPanel.setCellSpacing(10);
		buttonPanel.setWidget(0, 1, saveButton);
		buttonPanel.setWidget(0, 2, cancelButton);
		buttonPanel.getFlexCellFormatter().setWidth(0, 0, "100%");
	}

	private void initPassRepInput() {
		labels.add(passRepLabel);
		passRepBox.setWidth("12em");
	}

	private void initPasswordInput() {
		labels.add(passwordLabel);
		passwordBox.setWidth("12em");
	}

	private void initUsernameInput() {
		labels.add(usernameLabel);
		usernameBox.setWidth("12em");
	}

	private void initBirthDateInput() {
		labels.add(birthDateLabel);
		DateTimeFormat fullDateFormat = DateTimeFormat.getLongDateFormat();
		birthDateBox.setFormat(new DefaultFormat(fullDateFormat));
	}

	private void initGenderInput() {
		labels.add(genderLabel);

		genderPanel.setSpacing(5);
		maleButton.setValue(true);
		genderPanel.add(maleButton);
		genderPanel.add(femaleButton);
	}

	private void initEmailInput() {
		labels.add(emailLabel);
	}

	private void initPhoneNoInput() {
		labels.add(phoneNoLabel);
	}

	private void initCountryInput() {
		labels.add(countryLabel);

		javaService.getCountries(new AsyncCallback<Map<String, String>>() {
			@Override
			public void onSuccess(Map<String, String> result) {
				for (Entry<String, String> entry : result.entrySet()) {
					countryBox.addItem(entry.getKey(), entry.getValue());
				}
			}

			@Override
			public void onFailure(Throwable caught) {
				Log.error(caught.getMessage(), caught);
			}
		});

		countryLabel.setVisible(false);
		countryBox.setVisible(false);
	}

	private void initIcPassportInput() {
		labels.add(icPassportLabel);
	}

	private void initNationalityInput() {
		labels.add(nationalityLabel);

		nationalityPanel.setSpacing(5);
		malaysianButton.setValue(true);
		nationalityPanel.add(malaysianButton);
		nationalityPanel.add(otherButton);

		ValueChangeHandler<Boolean> nationalityGroupHandler = new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				if (event.getValue()) {
					boolean malaysian = event.getSource() == malaysianButton;
					icPassportLabel
							.setText(malaysian ? IC_TEXT : PASSPORT_TEXT);
					countryLabel.setVisible(!malaysian);
					countryBox.setVisible(!malaysian);
				}
			}
		};
		malaysianButton.addValueChangeHandler(nationalityGroupHandler);
		otherButton.addValueChangeHandler(nationalityGroupHandler);
	}

	private void initLastNameInput() {
		labels.add(lastNameLabel);
	}

	private void initFirstNameInput() {
		labels.add(firstNameLabel);
	}

	private void formatLabel(Label label) {
		Style style = label.getElement().getStyle();
		style.setFontWeight(FontWeight.BOLD);
		label.setHorizontalAlignment(Util.getReverseAlignment(ALIGN_DEFAULT));
	}

	private void logSteps(DirectionResults result) {
		StringBuilder stepsSB = new StringBuilder();

		List<Placemark> marks = result.getPlacemarks();
		Placemark start = marks.get(0);
		Placemark end = marks.get(marks.size() - 1);

		stepsSB.append("Org:  \t").append(start.getPoint()).append(" \t") //
				.append(start.getAddress()) //
				.append('\n').append("Steps: \n");

		List<Route> routes = result.getRoutes();
		for (int i = 0; i < routes.size(); i++) {
			Route route = routes.get(i);
			stepsSB.append("Route #").append(i).append(":\n");
			for (int j = 0; j < route.getNumSteps(); j++) {
				Step step = route.getStep(j);
				stepsSB.append(step.getPolylineIndex()).append(" \t- ") //
						.append(step.getLatLng()) //
						.append(" \t- ") //
						.append(step.getDescriptionHtml()) //
						.append('\n');
			}
		}
		stepsSB.append("Dst:  \t").append(end.getPoint()).append(" \t") //
				.append(end.getAddress()) //
				.append('\n');

		stepsSB.append("\nSummary: ").append(result.getSummaryHtml());

		Log.debug(stepsSB.append('\n').toString());
	}

	private void logPolyline(DirectionResults result) {
		StringBuilder polylineSB = new StringBuilder("\nPolyline:\n");
		Polyline polyline = result.getPolyline();
		for (int i = 0; i < polyline.getVertexCount(); i++) {
			LatLng vertex = polyline.getVertex(i);
			polylineSB.append(i).append(" \t - ").append(vertex).append('\n');
		}
		Log.debug(polylineSB.append('\n').toString());
	}

	private void updateCommutingHabits(final DirectionResults result) {
		initCommuter();
		HashSet<CommutingHabitDto> habits = new HashSet<CommutingHabitDto>();

		CommutingHabitDto habit = new CommutingHabitDto();
		habit.setCommuter(commuterDto);

		List<Placemark> marks = result.getPlacemarks();

		LatLng org = marks.get(0).getPoint();
		habit.setOrigin(Util.toLatLon(org));

		LatLng dst = marks.get(marks.size() - 1).getPoint();
		habit.setDetination(Util.toLatLon(dst));

		ArrayList<WayPointDto> wayPoints = extractVertices(result);
		habit.setWayPointDtos(wayPoints);

		habits.add(habit);
		commuterDto.setCommutingHabits(habits);
	}

	private ArrayList<WayPointDto> extractVertices(final DirectionResults result) {

		ArrayList<WayPointDto> wayPoints = new ArrayList<WayPointDto>();
		Polyline polyline = result.getPolyline();
		for (int i = 0; i < polyline.getVertexCount(); i++) {
			WayPointDto wayPoint = new WayPointDto();
			wayPoint.setSequenceNo(i);
			LatLng vertex = polyline.getVertex(i);
			wayPoint.setPosition(Util.toLatLon(vertex));
			wayPoints.add(wayPoint);
		}
		return wayPoints;
	}

	//

	private final class MyDirectionsCallback implements DirectionsCallback {
		private final String link;

		private MyDirectionsCallback(String link) {
			this.link = link;
		}

		@Override
		public void onSuccess(final DirectionResults result) {
			String logMsg = "ENTERING onSuccess" + " OF loadFromWaypoints";
			Log.debug(logMsg);

			logPolyline(result);
			logSteps(result);

			final RouteConfirmDialog confirmDialog = new RouteConfirmDialog( //
					mapWidget, directionsPanel);
			// confirmDialog.setSize("90%", "90%");
			// confirmDialog.show();
			confirmDialog.center();

			CloseHandler<PopupPanel> closeHandler = new CloseHandler<PopupPanel>() {
				@Override
				public void onClose(CloseEvent<PopupPanel> event) {

					switch (confirmDialog.getResult()) {
					case CONFIRM:
						linkArea.setReadOnly(true);
						checkRouteButton.setEnabled(false);
						greenCheckImage.setVisible(true);

						updateCommutingHabits(result);
						break;
					case MODIFY:
						Window.open(link, "_new", null);
						break;
					}
				}

			};
			confirmDialog.addCloseHandler(closeHandler);

			Log.debug("EXITING onSuccess" //
					+ " OF loadFromWaypoints");
		}

		@Override
		public void onFailure(int statusCode) {
			String message = StatusCodes.getName(statusCode);
			RuntimeException ex = new RuntimeException(message);
			Log.error(message, ex);

			Window.alert(message);
		}
	}
}
